App.require(["matrix4", "vector3"], function (matrix4, vector3) {
    var Matrix4 = matrix4.Matrix4,
        Vector3 = vector3.Vector3;
    var v1 = new Vector3(),
        v2 = new Vector3(),
        v3 = new Vector3(),
        m1 = new Matrix4();

    function Trackball(constant) {
        this.constant = constant || 1;
        this.currX = 0;
        this.currY = 0;
        this.moving = false;

        this.matrix = new Matrix4();
        this.toMul = new Matrix4();

        this.canvas = null;
        this.mousedownHandler = this._mousedownHandler.bind(this);
        this.mouseupHandler = this._mouseupHandler.bind(this);
        this.mousemoveHandler = this._mousemoveHandler.bind(this);
    }
    Trackball.prototype = {
        constructor: Trackball,
        _mousedownHandler: function (ev) {
            this.currX = ev.clientX;
            this.currY = ev.target.height - ev.clientY;
            this.moving = true;
        },
        _mouseupHandler: function (ev) {
            this.moving = false;
        },
        _mousemoveHandler: function (ev) {
            if (!this.moving) {
                return;
            }
            var newX = ev.clientX;
            var newY = ev.target.height - ev.clientY;
            var dx = newX - this.currX;

            var rad = ev.target.width;
            v1.set(this.currX, this.currY, rad).normalize();
            v2.set(newX, newY, rad).normalize();
            Vector3.cross(v1, v2, v3);
            var angle = v3.length() * this.constant;
            if (angle > 0.00001) {
                Matrix4.fromRotationAxis(v3.normalize(), angle, m1);
                this.matrix.premultiply(m1);
            }
            Matrix4.transpose(this.matrix, this.toMul);
            this.currX = newX;
            this.currY = newY;
        },
        init: function (canvas) {
            this.canvas = canvas;
            canvas.addEventListener("mousedown", this.mousedownHandler, false);
            canvas.addEventListener("mouseup", this.mouseupHandler, false);
            canvas.addEventListener("mouseleave", this.mouseupHandler, false);
            canvas.addEventListener("mousemove", this.mousemoveHandler, false);
        },
        dispose: function () {
            if (this.canvas) {
                this.canvas.removeEventListener("mousedown", this.mousedownHandler, false);
                this.canvas.removeEventListener("mouseup", this.mouseupHandler, false);
                this.canvas.removeEventListener("mouseleave", this.mouseupHandler, false);
                this.canvas.removeEventListener("mousemove", this.mousemoveHandler, false);
            }
        }
    }

    App.define("webglTool", {
        Trackball: Trackball
    })
})